// ARMware - an ARM emulator
// Copyright (C) <2007>  Wei Hu <wei.hu.tw@gmail.com>
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

#ifndef Chunk_hpp
#define Chunk_hpp

#if ENABLE_THREADED_CODE || ENABLE_DYNAMIC_TRANSLATOR

#if PRINT_CHUNK_CODE
#include <iostream>
#endif

#include "MemoryPool.hpp"
#include "ARMInstInfo.hpp"
#include "HashTable.hpp"

#if ENABLE_DYNAMIC_TRANSLATOR
#include <vector>

#include "Compiler/Variable.hpp"
#include "Compiler/Label.hpp"
#include "Compiler/LiveInterval.hpp"
#include "Global.hpp"
#endif

namespace ARMware
{
  class BasicBlock;
  class MIR;
  class ConditionBlock;
  class Tuple;
  
  typedef class Chunk Chunk;
  class Chunk : public MemoryPool<Chunk, 128>
  {
  public:
    
    // Status
    
    enum Status
    {
      ST_NONE,
      ST_THREADED_CODE
#if ENABLE_DYNAMIC_TRANSLATOR
      , ST_DYNAMIC
#endif
    };
    typedef enum Status Status;
    
    static uint32_t const INITIAL_HIT_COUNT = 1;
    
  private:
    
    Status m_status;
    
    uint32_t m_start_paddr;
    uint32_t m_hit_count;
    
    uint32_t m_length;
    
    // :NOTE: Wei 2004-Oct-02:
    //
    // Threaded code buffer
    void *mp_tc_buffer;
    
    ARMInstInfo *mp_first_arm_inst_info;
    ARMInstInfo *mp_curr_arm_inst_info;
    
    void clean_tc_buffer();
    
#if ENABLE_THREADED_CODE || ENABLE_DYNAMIC_TRANSLATOR
    uint32_t m_arm_idx;
#endif
    
#if ENABLE_DYNAMIC_TRANSLATOR
    Label *mp_pending_back_patching_label_head;
    Label *mp_pending_back_patching_label_tail;
    
    std::vector<BasicBlock *> m_bb_table;
    
    // :NOTE: Wei 2004-Dec-31:
    //
    // The order of the global variables is the same with
    // Variable::GlobalVarEnum
    std::vector<Variable *> m_global_var_table;
    
    Variable *mp_global_var_head;
    Variable *mp_global_var_tail;
    
    Variable *mp_temp_var_head;
    Variable *mp_temp_var_tail;
    
    // :NOTE: Wei 2005-May-19:
    //
    // Used in SCC value numbering.
    std::vector<Variable *> m_memory_var_table;
    
    Variable *mp_memory_var_head;
    Variable *mp_memory_var_tail;
    
    // :NOTE: Wei 2005-May-23:
    //
    // For Available Expression analysis
    uint32_t m_memory_operation_number;
    
    std::vector<Variable *> m_used_var_table;
    
    // :NOTE: Wei 2004-Nov-12:
    //
    // The reason why I use link list to implement the temp variable table
    // is because I not only will add new temp variable to the temp variable
    // temp, but I also will remove temp variables from the table.
    //
    // Thus, in order to speed up the deletion operation, I use link list
    // to implement this temp variable table, rather than STL's container.
    uint32_t m_temp_var_number;
    
#if PRINT_CHUNK_CODE
    uint32_t m_temp_var_sequence_number;
#endif
    
    HashTable<Variable> m_const_table;
    
    std::vector<Label *> m_label_table;
    
    MIR *mp_n_bit_mir;
    MIR *mp_z_bit_mir;
    MIR *mp_c_bit_mir;
    MIR *mp_v_bit_mir;
    
    uint32_t m_LIR_number;
    
    // :NOTE: Wei 2004-Oct-19:
    //
    // This is the depth first number for every basic blocks.
    uint32_t m_df_number;
    
    // :NOTE: Wei 2004-Oct-20:
    //
    // The following fields are used in Lengauer-Tarjan algorithm
    // for calculating the dominator tree.
    std::vector<uint32_t> m_semi_dominator; // the df number of semi-dominator
    std::vector<uint32_t> m_dominator; // the df number of dominator
    
    std::vector<uint32_t> m_ancestor; // the df number
    std::vector<uint32_t> m_label; // the df number
    
    // :NOTE: Wei 2004-Oct-20:
    //
    // The df number of the vertex which is the parent in the spanning tree
    // generated by the search.
    std::vector<uint32_t> m_parent;
    
    std::vector<uint32_t> m_child; // the df number
    std::vector<uint32_t> m_size;
    
    // :NOTE: Wei 2004-Oct-20:
    //
    // The vertex whose number is i.
    std::vector<BasicBlock *> m_vertex;
    
    // :NOTE: Wei 2004-Oct-22:
    //
    // The following data structures are defined in Sreedhar & Gao:
    // 'A linear time algorithm for placing Phi-nodes'
    std::vector<bool> m_visited;
    std::vector<bool> m_alpha;
    std::vector<bool> m_in_phi;
    std::vector<uint32_t> m_dom_tree_level;
    uint32_t m_curr_level;
    
    std::vector<uint8_t> m_native_code_buffer;
    
    // :NOTE: Wei 2004-Oct-19:
    //
    // This is the ROOT node of the Depth-First Search spanning tree among all basic blocks.
    BasicBlock *mp_root_bb;
    
    BasicBlock *mp_prev_dfs_searching_bb;
    
    // :NOTE: Wei 2004-Oct-25:
    //
    // The following fields are used in live variable analysis (worklist algorithm)
    BasicBlock *mp_worklist_bb;
    
    // :NOTE: Wei 2005-May-26:
    //
    // Used for the cleanup.
    std::vector<MIR *> m_all_mir;
    
    MIR *mp_mir_head;
    MIR *mp_mir_tail;
    
    bool m_next_mir_is_leader;
    bool m_flags_no_change;
    
    std::vector<ConditionBlock *> m_cond_block_table;
    ConditionBlock *mp_cond_block;
    bool m_finish_setting_cond_block;
    BasicBlock *mp_curr_bb;
    
    InstCond m_old_cond;
    
    MIR *mp_cond_block_back_patching_add_mir;
    Label *mp_cond_block_back_patching_label;
    
    std::list<LIR *> m_all_lir_by_bfs;
    
    LiveInterval *mp_unhandled_head;
    LiveInterval *mp_unhandled_tail;
    
    uint32_t m_postorder_idx;
    
    BasicBlock *mp_postorder_bb_head;
    BasicBlock *mp_postorder_bb_tail;
    
    uint32_t m_scc_dfs_num;
    std::vector<BasicBlock *> m_scc_stack;
    
  public:
    
    static uint32_t const OPERATION_TABLE_SIZE = 128;
    
  private:
    
    std::list<Tuple *> m_operation_table[OPERATION_TABLE_SIZE];
        
    template<InstCond T_cond>
    void translate_cond_code();
    
    template<InstCond T_cond>
    void check_and_break_cond_block(Label * const label, bool &finish);
    
    template<InstCond T_cond>
    MIR *break_cond_block(MIR * const mir);
    
    void back_patching_pending_label();
    void back_patch_cond_block_back_patching_add_mir();
    void translate_chunk_to_MIR();
    void eliminate_redundant_cond_code_calculation_and_link_bb();
    
    void build_dfs_spanning_tree(BasicBlock * const bb);
    void compress(uint32_t const v);
    uint32_t eval(uint32_t const v);
    void link(uint32_t const v, uint32_t const w);
    
    void build_dominator_tree();
    
    void calculate_dominator_frontier();
    
    void compute_dominance_frontier(BasicBlock * const bb);
    
    void compute_iterated_dominance_frontier_for_var(Variable * const var);
    
    void determine_live_global_var();
    
    void insert_phi_function_internal(std::vector<Variable *> &vect);
    void insert_phi_function();
    
    template<Variable::KindEnum T_kind>
    void number_var_and_fill_table_internal(Variable * const head, uint32_t &idx);
    
    template<ConsiderMemoryVarEnum T_consider>
    void number_var_and_fill_table();
    
    template<ConsiderMemoryVarEnum T_consider>
    void build_worklist(BasicBlock * const bb,
                        uint32_t const num_of_bytes_needed_for_varlist);
    
    template<ConsiderMemoryVarEnum T_consider>
    void live_variable_analysis();
    
    void build_AVAIL_worklist(BasicBlock * const bb,
                              uint32_t const num_of_bytes_needed_for_varlist);
    
    void AVAIL_analysis();

    void numbering_lir_by_bfs();
    void sort_live_ranges();
    
    void convert_to_ssa_form();
    
    void generating_move_for_phi_operand();
    void add_live_range(Variable * const var, BasicBlock * const bb, uint32_t const end_idx);
    void computing_live_interval(BasicBlock * const bb);
    
    bool live_interval_is_compatible_internal(LiveInterval * const live1,
                                              LiveInterval * const live2);
    bool live_interval_is_compatible(LiveInterval * const live1,
                                     LiveInterval * const live2);
    
    void join_live_interval_internal(LiveInterval * const live1,
                                     LiveInterval * const live2);
    void preparing_for_linear_scan();
    
    void linear_scan_register_allocation_real();
    void linear_scan_register_allocation();
    
    void cleanup_variable(Variable * const head);
    void cleanup_compiler_intermedia_product();
    
#if CHECK_CHUNK_CODE
    void check_all_bb_not_in_worklist();
    
    void check_live_interval_and_lir_idx(LIR * const curr_lir, Variable * const var);
#endif
    
#if PRINT_CHUNK_CODE
    void dump_mir_info() const;
    
    template<bool T_ssa_form, bool T_use_global_idx, bool T_print_memory_operation_idx>
    void print_lir_code() const;
    
    void dump_live_var_info_internal(std::vector<uint32_t> const &vect) const;
    void dump_live_var_info() const;
    
    void dump_iterated_dominance_frontier_for_var(Variable * const var);
    void dump_iterated_dominance_frontier();
#endif
    
#endif // #if ENABLE_DYNAMIC_TRANSLATOR
    
    friend class MemoryPool<Chunk, 128>;
    
  public:
    
    // Life cycle
    
    inline
    Chunk(uint32_t const start_paddr)
      : m_status(ST_NONE),
        m_start_paddr(start_paddr),
        m_hit_count(INITIAL_HIT_COUNT),
        mp_tc_buffer(0)
#if ENABLE_DYNAMIC_TRANSLATOR
        , mp_global_var_head(0),
        mp_temp_var_head(0),
        mp_memory_var_head(0),
        m_memory_operation_number(0),
        m_temp_var_number(0),
#if PRINT_CHUNK_CODE
        m_temp_var_sequence_number(0),
#endif
        mp_mir_head(0)
#if CHECK_CHUNK_CODE
        , m_finish_setting_cond_block(false)
#endif
        , mp_unhandled_head(0),
        m_postorder_idx(0),
        mp_postorder_bb_head(0),
        m_scc_dfs_num(0)
#endif
    {
#if ENABLE_DYNAMIC_TRANSLATOR
      m_global_var_table.resize(Variable::GLOBAL_VAR_TOTAL);
      m_memory_var_table.resize(Variable::GLOBAL_VAR_TOTAL);
#endif
    }
    
    ~Chunk();
    
    // Operation
    
    BasicBlock *get_new_basicblock();
    
    Tuple *search_operation_table(Tuple const * const tuple);
    
    void build_postorder_list(BasicBlock * const bb);
    void find_scc_component(BasicBlock * const bb);
    
    bool value_numbering_one_bb(BasicBlock * const bb);
    void value_numbering_group_bb(BasicBlock * const bb);
    void scc_value_numbering(BasicBlock * const bb);
    
    void preform_sccvn();
    
    void fully_static_redundancy_elimination();
    
    inline void
    increase_hit_count()
    { ++m_hit_count; }
    
    inline void
    alloc_tc_buffer()
    {
      assert(0 == mp_tc_buffer);
      
      switch (m_length)
      {
      case   0:
      case   1:
      case   2:
      case   3:
      case   4:
      case   5:
      case   6:
      case   7:
      case   8:
      case   9:
        mp_tc_buffer = ARMInstInfo_10::operator new(0);
        mp_first_arm_inst_info = static_cast<ARMInstInfo_10 *>(mp_tc_buffer)->m_infos;
        break;
        
      case  10:
      case  11:
      case  12:
      case  13:
      case  14:
      case  15:
      case  16:
      case  17:
      case  18:
      case  19:
        mp_tc_buffer = ARMInstInfo_20::operator new(0);
        mp_first_arm_inst_info = static_cast<ARMInstInfo_20 *>(mp_tc_buffer)->m_infos;
        break;
        
      case  20:
      case  21:
      case  22:
      case  23:
      case  24:
      case  25:
      case  26:
      case  27:
      case  28:
      case  29:
        mp_tc_buffer = ARMInstInfo_30::operator new(0);
        mp_first_arm_inst_info = static_cast<ARMInstInfo_30 *>(mp_tc_buffer)->m_infos;
        break;
        
      case  30:
      case  31:
      case  32:
      case  33:
      case  34:
      case  35:
      case  36:
      case  37:
      case  38:
      case  39:
        mp_tc_buffer = ARMInstInfo_40::operator new(0);
        mp_first_arm_inst_info = static_cast<ARMInstInfo_40 *>(mp_tc_buffer)->m_infos;
        break;
        
      case  40:
      case  41:
      case  42:
      case  43:
      case  44:
      case  45:
      case  46:
      case  47:
      case  48:
      case  49:
        mp_tc_buffer = ARMInstInfo_50::operator new(0);
        mp_first_arm_inst_info = static_cast<ARMInstInfo_50 *>(mp_tc_buffer)->m_infos;
        break;
        
      case  50:
      case  51:
      case  52:
      case  53:
      case  54:
      case  55:
      case  56:
      case  57:
      case  58:
      case  59:
        mp_tc_buffer = ARMInstInfo_60::operator new(0);
        mp_first_arm_inst_info = static_cast<ARMInstInfo_60 *>(mp_tc_buffer)->m_infos;
        break;
        
      case  60:
      case  61:
      case  62:
      case  63:
      case  64:
      case  65:
      case  66:
      case  67:
      case  68:
      case  69:
        mp_tc_buffer = ARMInstInfo_70::operator new(0);
        mp_first_arm_inst_info = static_cast<ARMInstInfo_70 *>(mp_tc_buffer)->m_infos;
        break;
        
      case  70:
      case  71:
      case  72:
      case  73:
      case  74:
      case  75:
      case  76:
      case  77:
      case  78:
      case  79:
        mp_tc_buffer = ARMInstInfo_80::operator new(0);
        mp_first_arm_inst_info = static_cast<ARMInstInfo_80 *>(mp_tc_buffer)->m_infos;
        break;
        
      case  80:
      case  81:
      case  82:
      case  83:
      case  84:
      case  85:
      case  86:
      case  87:
      case  88:
      case  89:
        mp_tc_buffer = ARMInstInfo_90::operator new(0);
        mp_first_arm_inst_info = static_cast<ARMInstInfo_90 *>(mp_tc_buffer)->m_infos;
        break;
        
      case  90:
      case  91:
      case  92:
      case  93:
      case  94:
      case  95:
      case  96:
      case  97:
      case  98:
      case  99:
        mp_tc_buffer = ARMInstInfo_100::operator new(0);
        mp_first_arm_inst_info = static_cast<ARMInstInfo_100 *>(mp_tc_buffer)->m_infos;
        break;
        
      case 100:
      case 101:
      case 102:
      case 103:
      case 104:
      case 105:
      case 106:
      case 107:
      case 108:
      case 109:
        mp_tc_buffer = ARMInstInfo_110::operator new(0);
        mp_first_arm_inst_info = static_cast<ARMInstInfo_110 *>(mp_tc_buffer)->m_infos;
        break;
        
      case 110:
      case 111:
      case 112:
      case 113:
      case 114:
      case 115:
      case 116:
      case 117:
      case 118:
      case 119:
        mp_tc_buffer = ARMInstInfo_120::operator new(0);
        mp_first_arm_inst_info = static_cast<ARMInstInfo_120 *>(mp_tc_buffer)->m_infos;
        break;
        
      case 120:
      case 121:
      case 122:
      case 123:
      case 124:
      case 125:
      case 126:
      case 127:
      case 128:
      case 129:
        mp_tc_buffer = ARMInstInfo_130::operator new(0);
        mp_first_arm_inst_info = static_cast<ARMInstInfo_130 *>(mp_tc_buffer)->m_infos;
        break;
        
      case 130:
      case 131:
      case 132:
      case 133:
      case 134:
      case 135:
      case 136:
      case 137:
      case 138:
      case 139:
        mp_tc_buffer = ARMInstInfo_140::operator new(0);
        mp_first_arm_inst_info = static_cast<ARMInstInfo_140 *>(mp_tc_buffer)->m_infos;
        break;
        
      case 140:
      case 141:
      case 142:
      case 143:
      case 144:
      case 145:
      case 146:
      case 147:
      case 148:
      case 149:
        mp_tc_buffer = ARMInstInfo_150::operator new(0);
        mp_first_arm_inst_info = static_cast<ARMInstInfo_150 *>(mp_tc_buffer)->m_infos;
        break;
        
      case 150:
      case 151:
      case 152:
      case 153:
      case 154:
      case 155:
      case 156:
      case 157:
      case 158:
      case 159:
        mp_tc_buffer = ARMInstInfo_160::operator new(0);
        mp_first_arm_inst_info = static_cast<ARMInstInfo_160 *>(mp_tc_buffer)->m_infos;
        break;
        
      case 160:
      case 161:
      case 162:
      case 163:
      case 164:
      case 165:
      case 166:
      case 167:
      case 168:
      case 169:
        mp_tc_buffer = ARMInstInfo_170::operator new(0);
        mp_first_arm_inst_info = static_cast<ARMInstInfo_170 *>(mp_tc_buffer)->m_infos;
        break;
        
      case 170:
      case 171:
      case 172:
      case 173:
      case 174:
      case 175:
      case 176:
      case 177:
      case 178:
      case 179:
        mp_tc_buffer = ARMInstInfo_180::operator new(0);
        mp_first_arm_inst_info = static_cast<ARMInstInfo_180 *>(mp_tc_buffer)->m_infos;
        break;
        
      case 180:
      case 181:
      case 182:
      case 183:
      case 184:
      case 185:
      case 186:
      case 187:
      case 188:
      case 189:
        mp_tc_buffer = ARMInstInfo_190::operator new(0);
        mp_first_arm_inst_info = static_cast<ARMInstInfo_190 *>(mp_tc_buffer)->m_infos;
        break;
        
      case 190:
      case 191:
      case 192:
      case 193:
      case 194:
      case 195:
      case 196:
      case 197:
      case 198:
      case 199:
        mp_tc_buffer = ARMInstInfo_200::operator new(0);
        mp_first_arm_inst_info = static_cast<ARMInstInfo_200 *>(mp_tc_buffer)->m_infos;
        break;
        
      default:
        mp_tc_buffer = ::operator new(sizeof(ARMInstInfo) * m_length);
        mp_first_arm_inst_info = static_cast<ARMInstInfo *>(mp_tc_buffer);
        break;
      }
      
      mp_curr_arm_inst_info = mp_first_arm_inst_info;
    }
    
    inline void
    tc_buffer_append(Inst const inst, ARMInstInfo::ArgList const * const arg_list)
    {
      // :NOTE: Wei 2004-Sep-30:
      //
      // placement new.
      new(mp_curr_arm_inst_info) ARMInstInfo(inst, arg_list);
      
      ++mp_curr_arm_inst_info;
    }
    
#if ENABLE_DYNAMIC_TRANSLATOR
    inline uint32_t
    memory_operation_number()
    { return m_memory_operation_number++; }
    
    void init_dynamic_compiler();
    
    MIR *insert_switch_statement_DT(Variable * const value_var,
                                    Label * const table_base_label);
    MIR *incre_PC_DT();
    
    void rename_variable();
#endif
    
    inline void
    reset_arm_idx()
    { m_arm_idx = 0; }
    
    inline void
    increment_arm_idx()
    { ++m_arm_idx; }
    
    inline void
    set_curr_arm_inst(uint32_t const inst_idx)
    {
      m_arm_idx = inst_idx;
      
      mp_curr_arm_inst_info = (mp_first_arm_inst_info + inst_idx);
    }

#if ENABLE_DYNAMIC_TRANSLATOR
    template<Variable::KindEnum T_type>
    inline std::vector<Variable *> &
    var_table()
    {
      switch (T_type)
      {
      case Variable::GLOBAL: return m_global_var_table;
      case Variable::MEMORY: return m_memory_var_table;
        
      default:
        assert(!"Should not reach here.");
        return m_global_var_table;
      }
    }
    
    template<Variable::KindEnum T_type>
    inline Variable *&
    var_list_head()
    {
      switch (T_type)
      {
      case Variable::GLOBAL: return mp_global_var_head;
      case Variable::TEMP:   return mp_temp_var_head;
      case Variable::MEMORY: return mp_memory_var_head;
        
      default:
        assert(!"Should not reach here.");
        return mp_global_var_head;
      }
    }
    
    template<Variable::KindEnum T_type>
    inline Variable *&
    var_list_tail()
    {
      switch (T_type)
      {
      case Variable::GLOBAL: return mp_global_var_tail;
      case Variable::TEMP:   return mp_temp_var_tail;
      case Variable::MEMORY: return mp_memory_var_tail;
        
      default:
        assert(!"Should not reach here.");
        return mp_global_var_tail;
      }
    }
    
    template<Variable::KindEnum T_type>
    inline Variable *
    add_new_var(Variable * const var)
    {
      assert(T_type == var->kind());
            
      if (0 == var_list_head<T_type>())
      {
        var->set_prev_link_var(0);
        
        var_list_head<T_type>() = var;
      }
      else
      {
        var->set_prev_link_var(var_list_tail<T_type>());
        
        var_list_tail<T_type>()->set_next_link_var(var);
      }
      
      var_list_tail<T_type>() = var;
      var_list_tail<T_type>()->set_next_link_var(0);
      
      if (Variable::TEMP == T_type)
      {
#if PRINT_CHUNK_CODE
        var->set_sequence_idx(m_temp_var_sequence_number);
        
        ++m_temp_var_sequence_number;
#endif
        
        ++m_temp_var_number;
      }
      
      return var;
    }
    
    void join_live_interval(LiveInterval *live1,
                            LiveInterval *live2);
#endif
    
    // Access
    
    inline void
    set_length(uint32_t const length)
    { m_length = length; }
    
    inline void
    set_status(Status const status)
    { m_status = status; }
    
    // Inquery
    
#if ENABLE_DYNAMIC_TRANSLATOR
    inline std::vector<Variable *> const &
    used_var_table() const
    { return m_used_var_table; }
#endif
    
    inline Status
    status() const
    { return m_status; }
    
    inline uint32_t
    start_paddr() const
    { return m_start_paddr; }
    
    inline ARMInstInfo *
    first_arm_inst_info() const
    { return mp_first_arm_inst_info; }
    
    inline ARMInstInfo *
    curr_arm_inst_info() const
    {
      assert(static_cast<uint32_t>((mp_curr_arm_inst_info - mp_first_arm_inst_info)) < m_length);
      
      return mp_curr_arm_inst_info;
    }
    
    inline ARMInstInfo *
    next_arm_inst_info() const
    {
      assert(static_cast<uint32_t>((mp_curr_arm_inst_info + 1 - mp_first_arm_inst_info)) < m_length);
      
      return mp_curr_arm_inst_info + 1;
    }
    
    inline ARMInstInfo *
    peek_arm_inst_info(uint32_t const arm_idx) const
    {
      assert(arm_idx < m_length);
      
      return mp_first_arm_inst_info + arm_idx;
    }
    
    // :NOTE: Wei 2004-Aug-31:
    //
    // Although this function is identical to the upper 'start_paddr()',
    // however, this function is needed by HashTable.
    inline uint32_t
    hash_value() const
    { return m_start_paddr; }
    
    inline uint32_t
    hit_count() const
    { return m_hit_count; }
    
#ifndef NDEBUG
    inline void *
    tc_buffer() const
    { return mp_tc_buffer; }
#endif
    
    inline uint32_t
    length() const
    { return m_length; }
    
    inline uint32_t
    inst_idx() const
    { return m_arm_idx; }
    
#if ENABLE_DYNAMIC_TRANSLATOR
    inline void
    add_pending_back_patch_label(Label * const label)
    {
      if (0 == mp_pending_back_patching_label_head)
      {
        mp_pending_back_patching_label_head = label;
      }
      else
      {
        mp_pending_back_patching_label_tail->set_next_need_back_patch_label(label);
      }
      
      mp_pending_back_patching_label_tail = label;
    }
    
    void emit_native_code_real(BasicBlock * const bb);
    void emit_native_code(BasicBlock * const bb);
    
    void link_labels();
    
#if CHECK_CHUNK_CODE
    void check_all_used_labels_linked();
    void check_bb_link_relationship();
#endif
    
    void gen_dt_code();
    
    inline void
    next_mir_is_leader()
    { m_next_mir_is_leader = true; }
    
    void check_end_a_cond_block(uint32_t const changed_cond);
    
    template<MIRAppendType T_type>
    MIR *append_mir(MIR * const mir);
    
    inline uint32_t
    scratch_reg_stack_offset() const
    {
      return -((m_temp_var_number + 1) << 2);
    }
    
    uint32_t stack_size() const;
    
#if CHECK_CHUNK_CODE
    void check_ssa_form(BasicBlock * const bb);
#endif
    
    inline Variable *
    get_new_temp(bool const act_as_global_var = false)
    {
      // :NOTE: Wei 2004-Oct-23:
      //
      // Temp variable is counted from 0.
      return add_new_var<Variable::TEMP>(new Variable(static_cast<VarTemp *>(0), act_as_global_var));
    }
    
    inline void
    remove_temp_var(Variable * const var)
    {
      assert(Variable::TEMP == var->kind());
      assert(0 == var->var_stack().size());
      
#if CHECK_CHUNK_CODE
      assert(m_temp_var_number != 0);
      assert((1 == m_temp_var_number) ? (mp_temp_var_head == var) : true);
#endif
      
      if (var == mp_temp_var_head)
      {
        mp_temp_var_head = var->next_link_var();
        
        if (mp_temp_var_head != 0)
        {
          mp_temp_var_head->set_prev_link_var(0);
        }
        else
        {
#if CHECK_CHUNK_CODE
          assert(1 == m_temp_var_number);
#endif
          
          mp_temp_var_tail = 0;
        }
      }
      else if (var == mp_temp_var_tail)
      {
        mp_temp_var_tail = var->prev_link_var();
        
        // :NOTE: Wei 2004-Nov-12:
        //
        // I could avoid checking whether mp_temp_var_tail == 0,
        // because if there is only one temp variable in this chunk,
        // then if I want to remove that one, I will go into the upper
        // if statement.
        //
        // Thus, if I go here, it means there are more than 1 temp variable
        // in the temp variable list.
        mp_temp_var_tail->set_next_link_var(0);
      }
      else
      {
        var->prev_link_var()->set_next_link_var(var->next_link_var());
        var->next_link_var()->set_prev_link_var(var->prev_link_var());
      }
      
      delete var;
      
      --m_temp_var_number;
    }
    
    inline Variable *
    find_global_var(Variable::GlobalVarEnum const T_var)
    {
      if (0 == m_global_var_table[T_var])
      {
        m_global_var_table[T_var] = add_new_var<Variable::GLOBAL>(new Variable(static_cast<VarGlobal *>(0), T_var));
      }
      
      return m_global_var_table[T_var];
    }
    
    inline Variable *
    find_memory_var(Variable::GlobalVarEnum const T_var)
    {
      if (0 == m_memory_var_table[T_var])
      {
        m_memory_var_table[T_var] = add_new_var<Variable::MEMORY>(new Variable(static_cast<VarMemory *>(0), T_var));
      }
      
      return m_memory_var_table[T_var];
    }
    
    inline Variable *
    global_var_head() const
    { return mp_global_var_head; }
    
    inline Variable *
    temp_var_head() const
    { return mp_temp_var_head; }
    
    inline Variable *
    find_const_var(int32_t const value)
    {
      uint32_t idx = 0;
      Variable *const_var = m_const_table.find_one_no_create(value, &idx);
      
      if (0 == const_var)
      {
        const_var = new Variable(static_cast<VarConst *>(0), value);
        
        m_const_table.add_new_element(const_var, idx);
      }
      
      return const_var;
    }
    
    inline Label *
    add_new_label(Label * const label)
    {
      m_label_table.push_back(label);
      
      return label;
    }
    
    inline uint8_t const *
    native_code_begin() const
    { return &(m_native_code_buffer.front()); }
#endif
  };
  
  // Global object pointer
  extern Chunk *gp_chunk;
}
#endif // ENABLE_THREADED_CODE

#endif
